﻿using System;
using System.Reflection;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Windows.Input;
using Windows.UI.Xaml;

namespace MenusHunter.WinRT.Core.MVVM
{
    /// <summary>
    /// 
    /// </summary>
    public class Behaviors:FrameworkElement
    {
        #region ReplayCommand
        /// <summary>
        /// 
        /// </summary>
        public static readonly DependencyProperty CommandProperty =
        DependencyProperty.Register("Command", typeof(ICommand), typeof(Behaviors),
                                    new PropertyMetadata(DependencyProperty.UnsetValue));

        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }
        #endregion

        #region RelayCommandParameter
        public static readonly DependencyProperty CommandParameterProperty =
       DependencyProperty.Register("CommandParameter", typeof(object), typeof(Behaviors),
                                   new PropertyMetadata(DependencyProperty.UnsetValue));

        public object CommandParameter
        {
            get { return GetValue(CommandParameterProperty); }
            set { SetValue(CommandParameterProperty, value); }
        }
 
        #endregion
        #region RelayCommandEvent
        /// <summary>
        /// 
        /// </summary>
        public static readonly DependencyProperty EventProperty =
        DependencyProperty.Register("Event", typeof(string), typeof(Behaviors),
                                    new PropertyMetadata(DependencyProperty.UnsetValue));

        private FrameworkElement _element;

        /// <summary>
        /// 
        /// </summary>
        public string Event
        {
            get { return (string)GetValue(EventProperty); }
            set { SetValue(EventProperty, value); }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <exception cref="ArgumentException"></exception>
        public FrameworkElement Element
        {
            get { return _element; }
            set
            {
                _element = value;

                var eventinfo = _element.GetType().GetRuntimeEvent(Event);

                // if event not found, throw exception
                if (eventinfo == null)
                    throw new ArgumentException(string.Format("Event {0} not found on element {1}.", Event, Element.GetType().Name));

                //Get method to call when event raised
                var executemethodinfo = GetType().GetTypeInfo().GetDeclaredMethod("ExecuteCommand").CreateDelegate(eventinfo.EventHandlerType, this);

                // Register event
                WindowsRuntimeMarshal.AddEventHandler(
                    del => (EventRegistrationToken)eventinfo.AddMethod.Invoke(_element, new object[] { del }),
                    token => eventinfo.RemoveMethod.Invoke(_element, new object[] { token }), executemethodinfo);

            }
        }
        private static void OnEventToCommandEventChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {

            // Get event info  
            var eventInfo = d.GetType().GetRuntimeEvent(e.NewValue.ToString());

            // Get method info
            var @delegate = typeof(Behaviors).GetTypeInfo().GetDeclaredMethod("ExecuteCommand")
                    .CreateDelegate(eventInfo.EventHandlerType);

            // Register to event
            WindowsRuntimeMarshal.AddEventHandler<Delegate>(del =>
               (EventRegistrationToken)eventInfo.AddMethod.Invoke(d, new object[] { del }),
                token => eventInfo.RemoveMethod.Invoke(d, new object[] { token }), @delegate);
        }
        #endregion
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="eventhandler"></param>
        public void ExecuteCommand(object sender, object eventhandler)
        {
            // The typed parameter

            // Set the DataContext to the element DataContext (Otherwise the binding returns NULL
            DataContext = Element.DataContext;

            //Command is null, so don't do anything
            if (Command == null)
                return;

            Command.Execute(CommandParameter);
        }
    }
}
